/**
 * @author phoenics@126.com
 * @version v.0.1
 * @date 2018年3月14日 下午2:06:56
 */
package com.github.phoenics.militia.common.auth.jwt;

import com.github.phoenics.militia.common.auth.TokenAuthentication;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.impl.DefaultJwtBuilder;
import io.jsonwebtoken.impl.DefaultJwtParser;
import com.github.phoenics.militia.common.auth.PasswordText;
import com.github.phoenics.militia.common.auth.PasswordTextFun;

import javax.annotation.PostConstruct;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.time.ZonedDateTime;
import java.util.Date;
import java.util.Optional;
import java.util.UUID;

import static java.util.Optional.empty;


/**
 *
 */
public  class JwtService {
    private static org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(JwtService.class);
    //private String keystore;
    private String keypass;
    private String keyalias;
    //private String cert;

    private PrivateKey privateKey;
    private PublicKey publicKey;

    private InputStream keystoreStream;
    private InputStream certStream;

    public JwtService(InputStream keystoreStream, String keypass, String keyalias, InputStream certStream) {
        this.keystoreStream=keystoreStream;
        this.keypass=keypass;
        this.keyalias=keyalias;
        this.certStream=certStream;
    }


    @PostConstruct
    private void init() throws Exception {
        char[] pass = keypass.toCharArray();
        KeyStore from = KeyStore.getInstance("JKS", "SUN");
        from.load( this.keystoreStream, pass);
        privateKey = (PrivateKey) from.getKey(keyalias, pass);
        CertificateFactory certificatefactory = CertificateFactory.getInstance("X.509");
        X509Certificate x509Cert = (X509Certificate) certificatefactory.generateCertificate(this.certStream);
        publicKey = x509Cert.getPublicKey();
    }

    public String generate(String context,Date date) {
        if(date ==null){
            date=Date.from(ZonedDateTime.now().plusWeeks(1).toInstant());
        }
        return new DefaultJwtBuilder()
                .setId(UUID.randomUUID().toString())
                .setSubject(context)
                .setExpiration(date)
                .signWith(SignatureAlgorithm.RS256, privateKey).compact();
    }
    public String generate(String context) {
        return generate(context,null);
    }

    public <U extends PasswordText,T extends PasswordTextFun> Optional<TokenAuthentication<U>> parse(String token, T t) throws ExpiredJwtException{
        Jws<Claims> jws = new DefaultJwtParser().setSigningKey(publicKey).parseClaimsJws(token);
        Claims claims = jws.getBody();
        return t.getPasswordText(claims.getSubject()).map(e -> new TokenAuthentication(e, token, claims));
    }

    public Optional<Claims> parse4Claims(String token) {
        Optional<Claims> claims = empty();
        try {
            Jws<Claims> jws = new DefaultJwtParser().setSigningKey(publicKey).parseClaimsJws(token);
            claims = Optional.ofNullable(jws.getBody());
        } catch (Exception e) {
            logger.error("failed to parse jwt token {}", token, e);
        }
        return claims;
    }
    public Long ExpirationDate(String token){
        return parse4Claims(token).map(c->c.getExpiration().getTime()).orElse(null);
    }
    public String newToken(String token,Long time){
        if(time==null){
            return token;
        }
        return parse4Claims(token).map(c->takeNewToken(c,time)).orElse(null);
    }
    private String takeNewToken(Claims claims,Long ext){
        Long time=claims.getExpiration().getTime()+ext;
        String sub=claims.getSubject();
        return generate(sub,new Date(time));
    }

}
